
Function ExecuteTestFunctions
{
    Param($functionNames)

    $testResults = @()
    ForEach($functionName in $functionNames)
    {
        if($functionName.StartsWith("Test_"))
        {
            $obj = New-Object Object |
                Add-Member NoteProperty TestName $functionName.Substring("Test_".length) -PassThru |
                Add-Member NoteProperty Passed $false -PassThru |
                Add-Member NoteProperty AnotherMessage "" -PassThru |
                Add-Member NoteProperty ExceptionMessage "" -PassThru

            Try
            {
                Invoke-Expression $functionName
                $obj.Passed = $true
            }
            Catch
            {
                $obj.Passed = $false
                $obj.ExceptionMessage = $_.Exception.ToString().Substring("System.Management.Automation.RuntimeException: ".length)
            }
            $testResults += $obj
        }
    }

    return ,$testResults
}

Function ExecuteTests
{
    Param($hideSummary=$false)

    $testResults = ExecuteTestFunctions (Get-ChildItem function:* -name)
    $numPassed = 0
    $numFailed = 0

    ForEach($testResult in $testResults)
    {
        if($testResult.Passed)
        {
            $numPassed += 1
            $s = "PASS   {0}" -f $testResult.TestName
            Write-Host $s -f green
        }
        else
        {
            $numFailed += 1
            $s = "FAIL   {0}" -f $testResult.TestName
            Write-Host $s -f red
            $s = "{0}" -f $testResult.ExceptionMessage
            Write-Host $s -f yellow
        }
    }

    if(!$hideSummary)
    {
        Write-Host "Passed: $numPassed  " -f green -NoNewLine
        Write-Host "Failed: $numFailed" -f red
    }
}

Function AssertThat
{
    Param($x, $y)

    if(!$y.Matches($x))
    {
        $frame = Get-PSCallStack | Where-Object { $_.Command.StartsWith("Test_") }
        $s = "{0}`n{1}" -f $y.Description, $frame.Location
        throw $s
    }
}

Function New-Matcher
{
    return New-Object Object |
        Add-Member NoteProperty Description "" -PassThru |
        Add-Member NoteProperty Value "" -PassThru
}

Function Anything
{
    return New-Object Object |
        Add-Member NoteProperty Description "" -PassThru |
        Add-Member ScriptMethod Matches {
            Param($y)
            $this.Description = "Tautological match"
            return ($false)
        } -PassThru 
}

Function Contains
{
    Param($x)

    return New-Object Object |
        Add-Member NoteProperty Value $x -PassThru |
        Add-Member NoteProperty Description "" -PassThru |
        Add-Member ScriptMethod Matches {
            Param($y)
            $this.Description = "'{0}' should contain '{1}'" -f $y, $this.Value
            return ($y.Contains($this.Value))
        } -PassThru |
        Add-Member ScriptMethod NotMatches {
            Param($y)
            $this.Description = "'{0}' should not contain '{1}'" -f $y, $this.Value
            return (!$y.Contains($this.Value))
        } -PassThru 
}

Function EndsWith
{
    Param($x)

    return New-Object Object |
        Add-Member NoteProperty Value $x -PassThru |
        Add-Member NoteProperty Description "" -PassThru |
        Add-Member ScriptMethod Matches {
            Param($y)
            $this.Description = "'{0}' should end with '{1}'" -f $y, $this.Value
            return ($y.EndsWith($this.Value))
        } -PassThru |
        Add-Member ScriptMethod NotMatches {
            Param($y)
            $this.Description = "'{0}' should not end with '{1}'" -f $y, $this.Value
            return (!$y.EndsWith($this.Value))
        } -PassThru 
}

Function EqualTo
{
    Param($x)

    return New-Object Object |
        Add-Member NoteProperty Value $x -PassThru |
        Add-Member NoteProperty Description "" -PassThru |
        Add-Member ScriptMethod Matches {
            Param($y)
            $this.Description = "'{0}' should be equal to '{1}'" -f $y, $this.Value
            return ($y -eq $this.Value)
        } -PassThru |
        Add-Member ScriptMethod NotMatches {
            Param($y)
            $this.Description = "'{0}' should not be equal to '{1}'" -f $y, $this.Value
            return ($y -ne $this.Value)
        } -PassThru 
}

Function GreaterThan
{
    Param($x)

    return New-Object Object |
        Add-Member NoteProperty Value $x -PassThru |
        Add-Member NoteProperty Description "" -PassThru |
        Add-Member ScriptMethod Matches {
            Param($y)
            $this.Description = "'{0}' should be greater than '{1}'" -f $y, $this.Value
            return ($y -gt $this.Value)
        } -PassThru |
        Add-Member ScriptMethod NotMatches {
            Param($y)
            $this.Description = "'{0}' should not be greater than '{1}'" -f $y, $this.Value
            return !($y -gt $this.Value)
        } -PassThru 
}

Function IsFalse
{
    Param($x)
    return New-Object Object |
        Add-Member NoteProperty Value $x -PassThru |
        Add-Member NoteProperty Description "" -PassThru |
        Add-Member ScriptMethod Matches {
            Param($y)
            $this.Description = "'{0}' should be FALSE" -f $y
            return ($y -eq $false)
        } -PassThru |
        Add-Member ScriptMethod NotMatches {
            Param($y)
            $this.Description = "'{0}' should be TRUE" -f $y
            return ($y -eq $true)
        } -PassThru 
}

Function IsNull
{
    Param($x)

    return New-Object Object |
        Add-Member NoteProperty Value $x -PassThru |
        Add-Member NoteProperty Description "" -PassThru |
        Add-Member ScriptMethod Matches {
            Param($y)
            $this.Description = "'{0}' should be NULL" -f $y
            return ($y -eq $null)
        } -PassThru |
        Add-Member ScriptMethod NotMatches {
            Param($y)
            $this.Description = "`$null should not be NULL"
            return ($y -ne $null)
        } -PassThru 
}

Function IsTrue
{
    Param($x)

    return New-Object Object |
        Add-Member NoteProperty Value $x -PassThru |
        Add-Member NoteProperty Description "" -PassThru |
        Add-Member ScriptMethod Matches {
            Param($y)
            $this.Description = "'{0}' should be TRUE" -f $y
            return ($y -eq $true)
        } -PassThru |
        Add-Member ScriptMethod NotMatches {
            Param($y)
            $this.Description = "'{0}' should be FALSE" -f $y
            return ($y -eq $false)
        } -PassThru 
}

Function Not
{
    Param($x)

    return New-Object Object |
        Add-Member NoteProperty Value $x -PassThru |
        Add-Member NoteProperty Description "" -PassThru |
        Add-Member ScriptMethod Matches {
            Param($y)
            $negatedResult = $this.Value.NotMatches($y)
            $this.Description = $this.Value.Description
            return $negatedResult
        } -PassThru 
}

Function StartsWith
{
    Param($x)

    return New-Object Object |
        Add-Member NoteProperty Value $x -PassThru |
        Add-Member NoteProperty Description "" -PassThru |
        Add-Member ScriptMethod Matches {
            Param($y)
            $this.Description = "'{0}' should start with '{1}'" -f $y, $this.Value
            return ($y.StartsWith($this.Value))
        } -PassThru |
        Add-Member ScriptMethod NotMatches {
            Param($y)
            $this.Description = "'{0}' should not start with '{1}'" -f $y, $this.Value
            return (!$y.StartsWith($this.Value))
        } -PassThru 
}

# vim: ft=ps1
